package com.tinyseed.emcp.api.basicdata.service.impl;

import com.google.common.collect.Lists;
import com.tinyseed.emcp.api.app.common.Constant;
import com.tinyseed.emcp.api.basicdata.domain.Monitor;
import com.tinyseed.emcp.api.basicdata.domain.ProducePollute;
import com.tinyseed.emcp.api.basicdata.domain.VproducePollute;
import com.tinyseed.emcp.api.basicdata.domain.Workshop;
import com.tinyseed.emcp.api.basicdata.model.*;
import com.tinyseed.emcp.api.basicdata.repository.*;
import com.tinyseed.emcp.api.basicdata.service.DeviceGroupService;
import com.tinyseed.emcp.api.basicdata.transfer.MonitorMapper;
import com.tinyseed.emcp.api.basicdata.transfer.VproducePolluteMapper;
import com.tinyseed.hmxx.common.base.PageData;
import com.tinyseed.hmxx.common.utils.QueryHelp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.sql.Timestamp;
import java.util.*;

/***
 * @ClassName: DeviceGroupServiceImpl
 * @Description: TODO
 * @Auther: Harlan.Xiong
 * @Date: 2019-11-27 21:47
 * @version : V1.0
 */
@Service
@CacheConfig(cacheNames = "deviceGroup")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class DeviceGroupServiceImpl implements DeviceGroupService {

    private final EnterpriseRepository enterpriseRepository;
    private final WorkshopRepository workshopRepository;
    private final VproducePolluteRepository vproducePolluteRepository;
    private final MonitorRepository monitorRepository;
    private final ProducePolluteRepository producePolluteRepository;
    private final OrganizeRepository organizeRepository;

    public DeviceGroupServiceImpl(EnterpriseRepository enterpriseRepository, WorkshopRepository workshopRepository, VproducePolluteRepository vproducePolluteRepository, MonitorRepository monitorRepository, ProducePolluteRepository producePolluteRepository, OrganizeRepository organizeRepository) {
        this.enterpriseRepository = enterpriseRepository;
        this.workshopRepository = workshopRepository;
        this.vproducePolluteRepository = vproducePolluteRepository;
        this.monitorRepository = monitorRepository;
        this.producePolluteRepository = producePolluteRepository;
        this.organizeRepository = organizeRepository;
    }

    @Override
    @Cacheable
    public List<OrganizeNodeOutputDTO> findOrganizeTree() {
        // 读取层次数据结果集列表
        List dataList = new ArrayList();
        /**
         * 机构信息
         */
        List<Map<String, Object>> organizes = organizeRepository.findOrganizes();
        if (!CollectionUtils.isEmpty(organizes)) {
            organizes.forEach(organize -> {
                HashMap dataRecord = new HashMap();
                dataRecord.put("id", organize.get("id"));
                dataRecord.put("name", organize.get("organize_name"));
                dataRecord.put("pid", organize.get("parent_id"));
                dataRecord.put("type", Constant.TREE_NODE_ORGANIZE);
                dataList.add(dataRecord);
            });
        }

        /**
         * 企业信息
         */
        List<Map<String, Object>> enterprises = enterpriseRepository.findEnterprises();
        if (!CollectionUtils.isEmpty(enterprises)) {
            enterprises.forEach(enterprise -> {
                HashMap dataRecord = new HashMap();
                dataRecord.put("id", Constant.ENTERPRISE_ID_REFIX + enterprise.get("id"));
                dataRecord.put("name", enterprise.get("enterprise_name"));
                dataRecord.put("pid", enterprise.get("organize_id"));
                dataRecord.put("type", Constant.TREE_NODE_ENTERPRISE);
                dataList.add(dataRecord);
            });
        }

        /**
         * 车间信息
         */
        List<Map<String, Object>> workshops = workshopRepository.findWorks();
        if (!CollectionUtils.isEmpty(workshops)) {
            workshops.forEach(workshop -> {
                HashMap dataRecord = new HashMap();
                dataRecord.put("id", Constant.WORKSHOP_ID_REFIX + workshop.get("id"));
                dataRecord.put("name", workshop.get("shop_name"));
                dataRecord.put("pid", Constant.ENTERPRISE_ID_REFIX + workshop.get("enterprise_id"));
                dataRecord.put("type", Constant.TREE_NODE_WORKSHOP);
                dataList.add(dataRecord);
            });
        }

        // 节点列表（散列表，用于临时存储节点对象）
        HashMap nodeMap = new HashMap();
        // 根节点集合
        List<OrganizeNodeOutputDTO> nodes = Lists.newArrayList();

        // 根据结果集构造节点列表（存入散列表）
        for (Iterator it = dataList.iterator(); it.hasNext(); ) {
            Map dataRecord = (Map) it.next();
            OrganizeNodeOutputDTO node = new OrganizeNodeOutputDTO();
            node.setId(dataRecord.get("id").toString());
            node.setName(dataRecord.get("name").toString());
            if ("0".equals(dataRecord.get("pid"))) {
                node.setPid("0");
            } else {
                node.setPid(dataRecord.get("pid") == null ? "":dataRecord.get("pid").toString());
            }
            node.setType(dataRecord.get("type").toString());
            nodeMap.put(node.getId(), node);
        }
        // 构造无序的多叉树
        Set entrySet = nodeMap.entrySet();
        for (Iterator it = entrySet.iterator(); it.hasNext(); ) {
            OrganizeNodeOutputDTO node = (OrganizeNodeOutputDTO) ((Map.Entry) it.next()).getValue();
            if ("0".equals(node.getPid())) {
                nodes.add(node);
            } else {
                if (!StringUtils.isEmpty(nodeMap.get(node.getPid()))) {
                    ((OrganizeNodeOutputDTO) nodeMap.get(node.getPid())).addChild(node);
                }
            }
        }

        return nodes;
    }

    @Override
    public PageData page(OrganizeNodeInputDTO organizeNodeInputDTO, int size, int current) {
        MonitorQueryCriteria dict = new MonitorQueryCriteria();
        if (Constant.TREE_NODE_ENTERPRISE.equals(organizeNodeInputDTO.getType())) {
            String enterpriseId = organizeNodeInputDTO.getId().substring(Constant.ENTERPRISE_ID_REFIX.length(), organizeNodeInputDTO.getId().length());
            List<Workshop> workshops = workshopRepository.findAllByEnterpriseId(Long.valueOf(enterpriseId));
            if (!CollectionUtils.isEmpty(workshops)) {
                List<Long> shopIds = Lists.newArrayList();
                workshops.forEach(d -> {
                    shopIds.add(d.getId());
                });
                dict.setShopIds(shopIds);
            }
        } else {
            String shopId = organizeNodeInputDTO.getId().substring(Constant.WORKSHOP_ID_REFIX.length(), organizeNodeInputDTO.getId().length());
            dict.setShopId(Long.parseLong(shopId));
        }

        Pageable pageable = PageRequest.of(current, size);
        Page page = vproducePolluteRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, dict, criteriaBuilder), pageable);
        PageData pageData = new PageData();
        pageData.setStart(page.getNumber());
        pageData.setData(page.getContent());
        pageData.setTotal(page.getTotalElements());
        pageData.setLimit(page.getSize());
        return pageData;
     /*

        Page<VproducePollute> result = vproducePolluteRepository.findAll((Root<VproducePollute> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) -> {
            Predicate predicate = null;
                if (dict.getShopIds() != null) {
                    Path<Object> path = root.get("shopId");
                    CriteriaBuilder.In<Object> in = criteriaBuilder.in(path);
                    for (Long shopId : dict.getShopIds()) {
                        in.value(shopId);
                    }
                    predicate = in;
                }else if(dict.getShopId() != null){
                    predicate = criteriaBuilder.equal(root.get("shopId").as(Long.class), dict.getShopId());
                }else{
                    predicate = criteriaBuilder.equal(root.get("shopId").as(Long.class), -1);
                }
            return predicate;
        }, pageable);
        List<VproducePolluteOutputDTO> vproducePolluteInputDTOList = Lists.newArrayList();
        result.getContent().forEach(producePollute -> {
            vproducePolluteInputDTOList.add(VproducePolluteMapper.INSTANCE.mapModel2OutPut(producePollute));
        });
        PageData pageData = new PageData();
        pageData.setStart(result.getNumber());
        pageData.setData(vproducePolluteInputDTOList);
        pageData.setTotal(result.getTotalElements());
        pageData.setLimit(result.getSize());
        return pageData;*/
    }

    @Override
    public List<VproducePolluteOutputDTO> findIndustryByPage(int current, int size) {
        Page<VproducePollute> industries = vproducePolluteRepository.findAll(PageRequest.of(current, size));
        List<VproducePolluteOutputDTO> producePolluteOutputDTOList = Lists.newArrayList();
        industries.getContent().forEach(producePollute -> producePolluteOutputDTOList.add(VproducePolluteMapper.INSTANCE.mapModel2OutPut(producePollute)));
        return producePolluteOutputDTOList;
    }

    @Override
    public MonitorRelationOutputDTO getEditSelectData(Long shopId) {
        MonitorRelationOutputDTO monitorRelationOutputDTO = new MonitorRelationOutputDTO();
        List<MonitorOutputDTO> produceList = Lists.newArrayList();
        List<MonitorOutputDTO> polluteList = Lists.newArrayList();
        List<Monitor> monitorList = monitorRepository.findByShopId(shopId);
        monitorList.forEach(monitor -> {
            if (Constant.MONITOR_PRODUCE_TYPE.equals(monitor.getEquipmentType())) {
                produceList.add(MonitorMapper.INSTANCE.mapModel2OutPut(monitor));
            } else {
                polluteList.add(MonitorMapper.INSTANCE.mapModel2OutPut(monitor));
            }
        });

        if (!CollectionUtils.isEmpty(produceList)) {
            monitorRelationOutputDTO.setProduceList(produceList);
        }

        if (!CollectionUtils.isEmpty(polluteList)) {
            monitorRelationOutputDTO.setPolluteList(polluteList);
        }

        return monitorRelationOutputDTO;
    }

    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(MonitorRelationInputDTO monitorRelationInputDTO) {
        List<ProducePollute> producePolluteList = Lists.newArrayList();
        List<VproducePolluteInputDTO> produceList = monitorRelationInputDTO.getProduceData();
        List<VproducePolluteInputDTO> polluteList = monitorRelationInputDTO.getPolluteData();
        if (CollectionUtils.isEmpty(polluteList)) {
            produceList.forEach(produce -> {
                producePolluteRepository.deleteByProduceId(produce.getProduceId());
            });
        } else {
            produceList.forEach(produce -> {
                producePolluteRepository.deleteByProduceId(produce.getProduceId());
                polluteList.forEach(pollute -> {
                    ProducePollute producePollute = new ProducePollute();
                    producePollute.setProduceId(produce.getProduceId());
                    producePollute.setPolluteId(pollute.getPolluteId());
                    producePollute.setRelation(pollute.getRelation());
                    Timestamp nowTime = new Timestamp(new Date().getTime());
                    producePollute.setCreateTime(nowTime);
                    producePolluteList.add(producePollute);
                });
            });
        }

        if (!CollectionUtils.isEmpty(producePolluteList)) {
            producePolluteRepository.saveAll(producePolluteList);
        }
    }

    @Override
    public Integer delete(Long produceId, Long polluteId) {
        producePolluteRepository.deleteByProduceIdAndPolluteId(produceId, polluteId);
        return 1;
    }
}
